#pragma once

namespace ganix
{
    const int HX711_READ_NOT_READY = 1<<26;

    class Hx711 {
        public:

        enum MODE{MODE_A_128 = 1, MODE_B_32 = 2, MODE_A_64 = 3};

        Hx711(int _sck, int _data, MODE _mode): sck(_sck), data(_data), mode(_mode), data0(0) {
            pinMode(_sck, OUTPUT);
            pinMode(_data, INPUT);
            calibrate();
        }

        Hx711(int _sck, int _data) {
            new (this)Hx711(_sck, _data, MODE_A_128);
        }

        void calibrate(int count = 8) {
            int sum = 0;
            for (int i=0; i<count; i++) {
                sum += read(true);
                delay(50);
            }
            data0 = sum / count;
            Serial.printf("hx 711 calibrate finish, data0 = %d\n", data0);
        }

        int read(bool sync = false) {
            //init
            digitalWrite(sck, LOW);
            if (digitalRead(data) == HIGH) {
                if (sync) {
                    while (digitalRead(data) == HIGH) {}
                } else {
                    return HX711_READ_NOT_READY;
                }
            }
            //read
            int result = 0;
            for (int i=0; i<24; i++) {
                pulse();
                result <<= 1;
                result |= digitalRead(data);
            }
            //set next mode
            pulse(mode);
            //end
            return result - data0;
        }

        private:
        int sck, data;
        int data0;
        MODE mode;
        
        void pulse(int count = 1) {
            for (int i=0; i<count; i++) {
                digitalWrite(sck, HIGH);
                //0.2us ~ 50us
                digitalWrite(sck, LOW);
            }
        }
    };
} // namespace ganix
